#pragma once

#include <cassert>
#include <cstdlib>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

// 守护进程化 -- 自成会话(不受用户登录或注销影响)

#define DEV "/dev/null"     // 文件黑洞，吞噬一切东西，取也取不到

void daemonSelf(const char *cwdPath = nullptr)
{
    // 1. 忽略可能发生异常的信号(SIGPIPE) - 如果服务器正准备向客户端发送数据时，客户端退了，那么就会发生读端关闭，写端继续写的情况，那么就会受到信号而终止
    signal(SIGPIPE, SIG_IGN);

    // 2. 要让自己不是进程组的组长，才能调用setsid()
    if (fork() > 0)
        exit(0);
    // 子进程走到这儿 --- 守护进程，也叫精灵进程，是孤儿进程的一种(它的父进程已经exit()了)
    pid_t n = setsid();
    assert(n != -1);
    (void)n;

    // 3. 守护进程脱离终端，重定向之前打开过的3个默认文件(关闭也可以，但是有风险)
    int fd = open(DEV, O_RDWR);     // 以读写方式打开
    if(fd >= 0)
    {
        dup2(fd, 0);
        dup2(fd, 1);
        dup2(fd, 2);

        close(fd);
    }
    else
    {
        // 打开失败
        close(0);
        close(1);
        close(2);
    }

    // 4. (可选：改变进程工作路径)
    if(cwdPath)
        chdir(cwdPath);
}